{-
 - phc -- the open source PHP compiler
 - See doc/license/README.license for licensing information
 -
 - Definition of the HIR
 -
 - Note that the HIR does not support the 'declare' statement, which nobody
 - uses or can even define precisely. It does not even seem to be supported
 - by the interpreter.
 -}

{-
	Configuration
-}

prefix "HIR";
external class "Object";
use list "List";
use string "String";
use namespace "HIR";

{-
	Top-level structure	
-}

php_script ::= statement* ;

statement ::=
     class_def | interface_def | method  
   | return | static_declaration | global
   | try | throw 
	| label | goto | branch 
	| assign_var | assign_var_var | assign_array | push_array 
	;

{-
	Class and method definitions
-}

class_def ::= 
   class_mod CLASS_NAME extends:CLASS_NAME? implements:INTERFACE_NAME* member* ;
class_mod ::= "abstract"? "final"? ;

interface_def ::= INTERFACE_NAME extends:INTERFACE_NAME* member* ;

member ::= method | attribute ;

method ::= signature statement*? ;
signature ::= method_mod is_ref:"&"? METHOD_NAME formal_parameter* ;
method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ;
formal_parameter ::= type is_ref:"&"? VARIABLE_NAME static_value? ;
type ::= CLASS_NAME? ;

attribute ::= attr_mod VARIABLE_NAME static_value? ;
attr_mod ::= "public"? "protected"? "private"? "static"? "const"?  ;

{-
	Statements
-}

return ::= VARIABLE_NAME? ;

branch ::= VARIABLE_NAME iftrue:LABEL_NAME iffalse:LABEL_NAME;
goto ::= LABEL_NAME ;
label ::= LABEL_NAME ;

static_declaration ::= VARIABLE_NAME static_value? ;
global ::= variable_name ; 
variable_name ::= VARIABLE_NAME | variable_variable;

try ::= statement* catches:catch* ;
catch ::= CLASS_NAME VARIABLE_NAME statement* ;
throw ::= VARIABLE_NAME ;

assign_var     ::= lhs:VARIABLE_NAME is_ref:"&"? ignore_errors:"@"? rhs:expr ;
assign_array   ::= lhs:VARIABLE_NAME index:VARIABLE_NAME is_ref:"&"? rhs:VARIABLE_NAME ;
assign_var_var ::= lhs:VARIABLE_NAME                     is_ref:"&"? rhs:VARIABLE_NAME ;
push_array     ::= lhs:VARIABLE_NAME                     is_ref:"&"? rhs:VARIABLE_NAME ; 

{-
	Expressions
-}

expr ::=
     cast | unary_op | bin_op 
   | constant | instanceof
   | variable_name | index_array
	| method_invocation | new 
	| literal ;
	
literal ::= INT<long> | REAL<double> | STRING<String*> | BOOL<bool> | NULL<> ;
   
index_array       ::= VARIABLE_NAME index:VARIABLE_NAME ;
variable_variable ::= VARIABLE_NAME ; 

cast     ::= CAST VARIABLE_NAME ;
unary_op ::= OP VARIABLE_NAME ;
bin_op   ::= left:VARIABLE_NAME OP right:VARIABLE_NAME ; 

constant ::= CLASS_NAME? CONSTANT_NAME ;

instanceof ::= VARIABLE_NAME class_name ;

target ::= VARIABLE_NAME | CLASS_NAME ;

method_invocation ::= target? method_name actual_parameter* ;
method_name ::= METHOD_NAME | VARIABLE_NAME ;

actual_parameter ::= is_ref:"&"? target? VARIABLE_NAME array_indices:VARIABLE_NAME?* ;

new ::= class_name actual_parameter* ;
class_name ::= CLASS_NAME | VARIABLE_NAME ;

static_value ::= literal | static_array | constant ;
 
static_array      ::= static_array_elem* ;
static_array_elem ::= static_array_key? is_ref:"&"? val:static_value ;
static_array_key  ::= literal | constant ; 

{-
 - Additional structure 
 -}

node ::= 
	  php_script | statement | class_mod | member | signature | method_mod 
	| formal_parameter | type | attribute | attr_mod | variable_name | expr
	| target | method_name | actual_parameter
	| static_value | static_array | static_array_elem | static_array_key
	| catch | class_name 
	| CONSTANT_NAME | OP | CAST | LABEL_NAME | INTERFACE_NAME 
	;

{-
	Mixin
-}

#include "lib/List.h"
#include "lib/String.h"

class HIR_node : Object 
{
public:
	AttrMap* attrs;

	// Return the line number of the node (or 0 if unknown)
	int get_line_number()
	{
		Integer* i = dynamic_cast<Integer*>(attrs->get("phc.line_number"));
		if(i != NULL)
			return i->value();
		else
			return 0;
	}

	// Return the filename of the node (or NULL if unknown)
	String* get_filename()
	{
		return dynamic_cast<String*>(attrs->get("phc.filename"));
	}

	HIR_node()
	{
		// Constructor gets called because all classes inherit from
		// HIR_node virtually; also, because maketea knows HIR_node is
		// abstract, it won't add a constructor itself
		attrs = new AttrMap();
	}

	void clone_mixin_from(HIR_node* in)
	{
		attrs = in->attrs->clone();
	}

	void assert_mixin_valid()
	{
		assert(attrs != NULL);

		AttrMap::const_iterator i;
		for(i = attrs->begin(); i != attrs->end(); i++)
		{
			if ((*i).first != "phc.line_number"
				&& (*i).first != "phc.filename")
			{
				assert((*i).second != NULL);
			}
		}
	}

	bool is_mixin_equal(HIR_node* in)
	{
		// Compare line number and filename
		// (We can't compare the entire attrs map because Object cannot
		// necessarily be compared for equality)

		if(get_line_number() != in->get_line_number())
			return false;

		if(get_filename() == NULL)
		{
			if(in->get_filename() != NULL)
				return false;
		}
		else
		{
			if(*get_filename() != *in->get_filename())
				return false;
		}

		return true;
	}
};
